using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Net.Mail;
using System.Security.Cryptography;
using System.Text;
using System.Web.Security;
using KeyCore.Domain.Model;
using KeyCore.Framework.Contexts;
using KeyCore.Framework.Repositories.Interfaces;

namespace KeyCore.Framework.Repositories
{
    public class UserRepository : IUserRepository
    {
        private IUserContext _userContext;

        public UserRepository()
        {
            _userContext = new UserContext();
        }

        public MembershipUser CreateUser(string userName, string password, string email, bool isApproved)
        {

            // TODO 
            // duplicated username 
            // duplicated email

            User user = new User();
            user.UserName = userName;
            if (IsValidEmail(email))
                user.Email = email;
            user.IsLocked = false;
            user.IsApproved = isApproved;
            user.PasswordSalt = CreateSalt();
            user.Password = CreatePasswordHash(password, user.PasswordSalt);
            user.CreatedDate = DateTime.Now;
            //if user not activated create AccountActivationCode
            if (!isApproved)
            {
                user.AccountActivationCode = Guid.NewGuid().ToString();
            }
            user.UserRole = _userContext.UserRoles.Single(r => r.RoleName == "user");
            _userContext.Users.Add(user);
            Save();
            //przemyslec zasadnosc uzycia ??? co do roli detached context
            //(_userContext as DbContext).UpdateGraph(user,map=>map.AssociatedEntity(r=>r.UserRole));
            return GetUser(userName);
        }

        public MembershipUser GetUser(string userName)
        {
            return GetUserByName(userName);
        }


        public MembershipUser GetUser(Guid userId)
        {
            var result = from u in _userContext.Users where (u.UserId == userId) select u;

            if (result.Count() != 0)
                return ToMembershipUser(result.FirstOrDefault());
            return null;

        }


        public string GetUserNameByEmail(string email)
        {
            var result = from u in _userContext.Users where (u.Email == email) select u;
            if (result.Count() != 0)
                return result.FirstOrDefault().UserName;
            return "";
        }

        public void Save()
        {
            (_userContext as DbContext).SaveChanges();
        }

        private static string CreateSalt()
        {
            RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
            byte[] buff = new byte[32];
            rng.GetBytes(buff);

            return Convert.ToBase64String(buff);
        }

        private static string CreatePasswordHash(string password, string salt)
        {
            string saltAndPassword = String.Concat(password, salt);
            Byte[] bytes = Encoding.UTF8.GetBytes(saltAndPassword);
            Byte[] hashedBytes = SHA1.Create().ComputeHash(bytes);
            string hashedPassword = BitConverter.ToString(hashedBytes).Replace("-", "");

            return hashedPassword;
        }

        public MembershipUser GetUserByName(string userName)
        {
            var result  = from u in _userContext.Users where u.UserName == userName select u;
            if (result.Count() != 0)
                return ToMembershipUser(result.FirstOrDefault());
            return null;
        }

        public static MembershipUser ToMembershipUser(User user)
        {
            string _userName = user.UserName;
            Guid _providerUserKey = user.UserId;
            string _email = user.Email;
            string _passwordQuestion = "";
            string _comment = "";
            bool _isApproved = user.IsApproved;
            bool _isLockedOut = user.IsLocked;
            DateTime _creationDate = user.CreatedDate;
            DateTime _lastLoginDate = user.LastLoginDate ?? DateTime.Now;
            DateTime _lastActivityDate = DateTime.Now;
            DateTime _lastPasswordChangedDate = DateTime.Now;
            DateTime _lastLockedOutDate = DateTime.Now;

            MembershipUser membershipUser = new MembershipUser("CustomMembershipProvider",
                                                     _userName,
                                                     _providerUserKey,
                                                     _email,
                                                     _passwordQuestion,
                                                     _comment,
                                                     _isApproved,
                                                     _isLockedOut,
                                                     _creationDate,
                                                     _lastLoginDate,
                                                     _lastActivityDate,
                                                     _lastPasswordChangedDate,
                                                     _lastLockedOutDate);
            return membershipUser;
        }

        public static Boolean IsValidEmail(string input)
        {
            try
            {
                new MailAddress(input);
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }

        public void Dispose()
        {
            if (_userContext !=null)
                _userContext.Dispose();
        }

        public MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
        {
            var muc = new MembershipUserCollection();
            var result = (from u in _userContext.Users select u).OrderBy(o=>o.CreatedDate).Skip(pageSize*pageIndex).Take(pageSize);
            totalRecords = result.Count();
            if (totalRecords == 0) return null;
            foreach (var user in result)
            {
                muc.Add(GetUserByName(user.UserName));
            }
            return muc;
        }

        public void UpdateUser(MembershipUser membershipUser)
        {
            var result = from u in _userContext.Users where u.UserId == (Guid) membershipUser.ProviderUserKey select u;
            if (result.Count() != 0)
            {
                var user = result.FirstOrDefault();
                user.LastLoginDate = membershipUser.LastLoginDate;
                (_userContext as DbContext).UpdateGraph(user);
            }
        }

        public bool ValidateUser(string username, string password)
        {
            var result = from u in _userContext.Users where (u.UserName == username) select u;

            if (result.Count() != 0)
            {
                var dbuser = result.First();

                if (dbuser.Password == CreatePasswordHash(password, dbuser.PasswordSalt) && dbuser.IsApproved)
                {
                    dbuser.LastLoginDate = DateTime.Now;
                   ( _userContext as DbContext).UpdateGraph(dbuser);
                    (_userContext as DbContext).SaveChanges();
                    return true;
                }
            
                return false;
            }
            return false;
        }
    }
}